"""
This module does not need any authentication checks
a CA user can create signing requests to the administrator 
and also upload CSR's 

Todo:
        
* check if uploaded file is really a CSR in PEM format
* check if CSR is already there
* validate user input using formencode schema
* password protect CSRs for later key/cert downloading 
* add a module for key/cert downloading
"""

"""
    This file is part of project webCA

    webCA is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    webCA is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with webCA. If not, see <http://www.gnu.org/licenses/>.
""" 

import logging

from pylons import request, response, session, tmpl_context as c
from pylons.controllers.util import abort, redirect_to

from webca.lib.base import BaseController, render

# needed to access app_globals.py
from pylons import config  
# needed for form validation
import formencode, webca.controllers.tools as tools, subprocess, sys
from formencode import validators
# needed for link_to()
import webca.lib.helpers as h
import os, shutil

log = logging.getLogger(__name__)

class ClientController(BaseController):
    def make_req(self):
        """This function generates certificate signing request in PEM format 
        """
        # set title, page name and footer containing all the links
        c.title = 'Client Request Management'
        c.page_name = 'Client Request Management'
        c.footer = config['pylons.app_globals'].ca_version_string + ' | ' + h.link_to('CA Management', h.url (controller='admin', action='certs')) 
                
        c.stderr_value = ''
        
        # has CA already been initialised?
        if os.path.exists(config['pylons.app_globals'].database):
            # user hit the "Generate CSR" buttong 
            if request.params.get('submit') == 'make_req':
                # provide debug information 
                if config['ca_debug']: print 'make_req() -> make_req'
                
                # to some really basic input validations 
                country_validator       = validators.String(max=2)
                state_validator         = validators.NotEmpty()
                location_validator      = validators.NotEmpty()
                organisation_validator  = validators.NotEmpty()
                organisation_unit_validator  = validators.NotEmpty()
                common_name_validator   = validators.NotEmpty()
                email_validator         = validators.NotEmpty()
               
                # try to validate the input using formencode 
                try:
                    # strip spaces from the fields 
                    c.country           = request.params.get('country').strip()
                    c.state             = request.params.get('state').strip()
                    c.location          = request.params.get('location').strip()
                    c.organisation      = request.params.get('organisation').strip()
                    c.organisation_unit = request.params.get('organisation_unit').strip()
                    c.common_name       = request.params.get('common_name').strip()
                    c.email             = request.params.get('email').strip()
                    
                    country_validator.to_python(c.country) 
                    state_validator.to_python(c.state)
                    location_validator.to_python(c.location)
                    organisation_validator.to_python(c.organisation)
                    organisation_unit_validator.to_python(c.organisation_unit)
                    common_name_validator.to_python(c.common_name)
                    email_validator.to_python(c.email)
                    
                    # open the openssl database 
                    fh = open(config['pylons.app_globals'].database)
                    
                    # put the distinguished name together 
                    subject = tools.build_subject(
                                                  str(c.country), 
                                                  str(c.state), 
                                                  str(c.location), 
                                                  str(c.organisation),
                                                  str(c.organisation_unit), 
                                                  str(c.common_name),
                                                  str(c.email)
                                                  )
                    
                    # is there already such a request within the database? 
                    if subject in fh.read() or os.path.exists(config['pylons.app_globals'].csr_dir + '/' + str(c.common_name) + '.csr'):
                        if config['ca_debug']: print 'there has already been such a request: ' + subject
                        
                        c.stderr_value = 'there has already been such a request: ' + subject
                        
                        return render('make_req.mako')
                    
                    # there is no such request, put the command together     
                    cmd = [
                           config['pylons.app_globals'].openssl_bin, 
                           'req', 
                           '-new', 
                           '-nodes', 
                           '-keyout', config['pylons.app_globals'].key_dir + '/' + str(c.common_name) + '.key', 
                           '-out', config['pylons.app_globals'].csr_dir + '/' + str(c.common_name) + '.csr', 
                           '-subj', subject
                          ]
                
                    # debug information 
                    if config['ca_debug']: print cmd
                
                    # create the request 
                    try:
                        proc = subprocess.Popen(cmd, stderr=subprocess.PIPE)
                        c.stderr_value = proc.communicate()[1]
                                   
                        if proc.returncode < 0:
                            message = 'Child was terminated b signal', proc.returncode
                            print >> sys.stderr, message 
                        else:
                            message = 'Child returned', proc.returncode
                            print >> sys.stderr, message
                    except OSError, e:
                        message = 'Execution failed:', e
                        print >> sys.stderr, message
                 
                except formencode.Invalid, e:
                    if config['ca_debug']: print e
                    c.stderr_value = e 
                 
            # user wants to upload his own request 
            elif request.params.get('submit') == 'upload':
                # debug information 
                if config['ca_debug']: 
                    print 'make_req() -> upload'
                    print 'POST:', request.POST['filename']
                
                # has a file been chosen?
                if 'filename' in request.POST and request.POST['filename'] != '':
                    if config['ca_debug']: print 'filename not in request.POST'
    
                    # get file object from POSTed file 
                    file_object = request.POST['filename']
                    
                    # is there already such a file? 
                    if not os.path.exists(os.path.join(config['pylons.app_globals'].csr_dir, file_object.filename.lstrip(os.sep))):
                        # upload the data 
                        permanent_file = open(os.path.join(config['pylons.app_globals'].csr_dir, file_object.filename.lstrip(os.sep)), 'w')
                        shutil.copyfileobj(file_object.file, permanent_file)
                    
                        permanent_file.close()
                        
                        # give the user some information 
                        if config['ca_debug']: print 'Successfully uploaded: %s, size: %i' % (file_object.filename, len(file_object.value))
                        c.stderr_value = 'Successfully uploaded: %s, size: %i bytes' % (file_object.filename, len(file_object.value))
                    # there is already such a file, can not upload 
                    else:
                        print 'File does already exist, cannot upload'
                        c.stderr_value =  'File does already exist, cannot upload'
                # no file has been selected for upload 
                else:
                    if config['ca_debug']: print 'in else...'            
        
                    c.stderr_value = 'choose file first...'
        # this seems to be a fresh install
        else:
            if config['ca_debug']: print 'please bootstrap first'
            c.stderr_value = 'CA has not yet been initialised...please login as admin'
            
        return render('make_req.mako')
